{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ic4_occAAiAT"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Hub Authors.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "both",
        "id": "ioaprt5q5US7"
      },
      "outputs": [],
      "source": [
        "# Copyright 2019 The TensorFlow Hub Authors. All Rights Reserved.\n",
        "#\n",
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "#     http://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License.\n",
        "# =============================================================================="
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "yCl0eTNH5RS3"
      },
      "outputs": [],
      "source": [
        "#@title MIT License\n",
        "#\n",
        "# Copyright (c) 2017 François Chollet\n",
        "#\n",
        "# Permission is hereby granted, free of charge, to any person obtaining a\n",
        "# copy of this software and associated documentation files (the \"Software\"),\n",
        "# to deal in the Software without restriction, including without limitation\n",
        "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
        "# and/or sell copies of the Software, and to permit persons to whom the\n",
        "# Software is furnished to do so, subject to the following conditions:\n",
        "#\n",
        "# The above copyright notice and this permission notice shall be included in\n",
        "# all copies or substantial portions of the Software.\n",
        "#\n",
        "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
        "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
        "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
        "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
        "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
        "# DEALINGS IN THE SOFTWARE."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ItXfxkxvosLH"
      },
      "source": [
        "# 映画レビューを使ったテキスト分類"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MfBg1C5NB3X0"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/hub/tutorials/tf2_text_classification\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"> TensorFlow.orgで表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/hub/tutorials/tf2_text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"> Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/hub/tutorials/tf2_text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/hub/tutorials/tf2_text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード/a0}</a></td>\n",
        "  <td><a href=\"https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1\"><img src=\"https://www.tensorflow.org/images/hub_logo_32px.png\">TF Hub モデルを見る</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Eg62Pmz3o83v"
      },
      "source": [
        "このノートブックでは、映画レビューのテキストを使用して、レビューを*肯定的評価*と*否定的評価*に分類します。これは、機械学習の問題で広く適用されている、重要な分類手法である*二項*分類の例です。\n",
        "\n",
        "[Internet Movie Database](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb) より、50,000 件の映画レビューテキストが含まれる [IMDB データセット](https://www.imdb.com/)を使用します。これは、トレーニング用の 25,000 件のレビューとテスト用の 25,000 件のレビューに分割されます。トレーニングセットとテストセットは*均衡が保たれており*、肯定的評価と否定的評価が同じ割合で含まれます。\n",
        "\n",
        "このノートブックでは、TensorFlow でモデルを構築してトレーニングするための [tf.keras](https://www.tensorflow.org/guide/keras) という高レベル API と、転移学習用のライブラリ兼プラットフォームである [TensorFlow Hub](https://www.tensorflow.org/hub) を使用します。`tf.keras` を使用した、より高度なテキスト分類チュートリアルについては、[MLCC Text Classification Guide](https://developers.google.com/machine-learning/guides/text-classification/) をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qrk8NjzhSBh-"
      },
      "source": [
        "### その他のモデル\n",
        "\n",
        "テキスト埋め込みの生成に使用できる、より表現豊かなモデルや効率性の高いモデルを[こちら](https://tfhub.dev/s?module-type=text-embedding)でご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Q4DN769E2O_R"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2ew7HTbPpCJH"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "import tensorflow as tf\n",
        "import tensorflow_hub as hub\n",
        "import tensorflow_datasets as tfds\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "print(\"Version: \", tf.__version__)\n",
        "print(\"Eager mode: \", tf.executing_eagerly())\n",
        "print(\"Hub version: \", hub.__version__)\n",
        "print(\"GPU is\", \"available\" if tf.config.list_physical_devices('GPU') else \"NOT AVAILABLE\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iAsKG535pHep"
      },
      "source": [
        "## IMDB データセットをダウンロードする\n",
        "\n",
        "IMDB データセットは、<a>TensorFlow データセット</a>で提供されています。次のコードを使って、IMDB データセットをマシン（または Colab ランタイム）にダウンロードしてください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zXXx5Oc3pOmN"
      },
      "outputs": [],
      "source": [
        "train_data, test_data = tfds.load(name=\"imdb_reviews\", split=[\"train\", \"test\"], \n",
        "                                  batch_size=-1, as_supervised=True)\n",
        "\n",
        "train_examples, train_labels = tfds.as_numpy(train_data)\n",
        "test_examples, test_labels = tfds.as_numpy(test_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l50X3GfjpU4r"
      },
      "source": [
        "## データを確認する\n",
        "\n",
        "データの形式を確認してみましょう。各例は、映画レビューを表す文章と対応するラベルです。文章はまったく事前処理されていません。ラベルは 0 または 1 の整数値で、0 は否定的評価で 1 は肯定的評価を示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "y8qCnve_-lkO"
      },
      "outputs": [],
      "source": [
        "print(\"Training entries: {}, test entries: {}\".format(len(train_examples), len(test_examples)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RnKvHWW4-lkW"
      },
      "source": [
        "最初の 10 個のサンプルを出力しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QtTS4kpEpjbi"
      },
      "outputs": [],
      "source": [
        "train_examples[:10]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IFtaCHTdc-GY"
      },
      "source": [
        "最初の 10 個のラベルも出力しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tvAjVXOWc6Mj"
      },
      "outputs": [],
      "source": [
        "train_labels[:10]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LLC02j2g-llC"
      },
      "source": [
        "## モデルを構築する\n",
        "\n",
        "ニューラルネットワークは、レイヤーのスタックによって作成されています。これには、次の 3 つのアーキテクチャ上の決定が必要です。\n",
        "\n",
        "- どのようにテキストを表現するか。\n",
        "- モデルにはいくつのレイヤーを使用するか。\n",
        "- 各レイヤーにはいくつの*非表示ユニット*を使用するか。\n",
        "\n",
        "この例では、入力データは文章で構成されています。予測するラベルは、0 または 1 です。\n",
        "\n",
        "テキストの表現方法としては、文章を埋め込みベクトルに変換する方法があります。トレーニング済みのテキスト埋め込みを最初のレイヤーとして使用することで、次のような 2 つのメリットを得ることができます。\n",
        "\n",
        "- テキストの事前処理を心配する必要がない。\n",
        "- 転移学習を利用できる。\n",
        "\n",
        "この例では、[google/tf2-preview/gnews-swivel-20dim/1](https://www.tensorflow.org/hub) という、[TensorFlow Hub](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1) のモデルを使用します。\n",
        "\n",
        "このチュートリアルのためにテストできるモデルが、ほかに 3 つあります。\n",
        "\n",
        "- [google/tf2-preview/gnews-swivel-20dim-with-oov/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim-with-oov/1) - [google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1) と同じものですが、2.5% の語彙が OOV バケットに変換されています。これは、タスクの語彙とモデルの語彙が完全に重複しない場合に役立ちます。\n",
        "- [google/tf2-preview/nnlm-en-dim50/1](https://tfhub.dev/google/tf2-preview/nnlm-en-dim50/1) - 最大 100 万語彙サイズと 50 次元の大規模なモデルです。\n",
        "- [google/tf2-preview/nnlm-en-dim128/1](https://tfhub.dev/google/tf2-preview/nnlm-en-dim128/1) - 最大 100 万語彙サイズと 128 次元のさらに大規模なモデルです。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "In2nDpTLkgKa"
      },
      "source": [
        "では始めに、TensorFlow Hub モデル を使用して文章を埋め込む Keras レイヤーを作成し、いくつかの入力サンプルで試してみましょう。生成される埋め込みの出力形状は、`(num_examples, embedding_dimension)` であるところに注意してください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_NUbzVeYkgcO"
      },
      "outputs": [],
      "source": [
        "model = \"https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1\"\n",
        "hub_layer = hub.KerasLayer(model, output_shape=[20], input_shape=[], \n",
        "                           dtype=tf.string, trainable=True)\n",
        "hub_layer(train_examples[:3])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dfSbV6igl1EH"
      },
      "source": [
        "今度は、完全なモデルを構築しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xpKOoWgu-llD"
      },
      "outputs": [],
      "source": [
        "model = tf.keras.Sequential()\n",
        "model.add(hub_layer)\n",
        "model.add(tf.keras.layers.Dense(16, activation='relu'))\n",
        "model.add(tf.keras.layers.Dense(1))\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6PbKQ6mucuKL"
      },
      "source": [
        "レイヤーは順にスタックされて、分類器が構築されます。\n",
        "\n",
        "1. 最初のレイヤーは、TensorFlow Hub レイヤーです。このレイヤーは文章から埋め込みベクトルにマッピングするために、トレーニング済みの SavedModel を使用します。使用中のモデル（[google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1)）は文章をトークンに分割し、各トークンを埋め込んで、埋め込みを組み合わせます。その結果、次元は `(num_examples, embedding_dimension)` となります。\n",
        "2. この固定長の出力ベクトルは、16 個の非表示ユニットを持つ完全に接続された（`Dense`）レイヤーを介してパイプ処理されます。\n",
        "3. 最後のレイヤーは単一の出力ノードで密に接続されます。これは、ロジットを出力します。モデルに応じた、真のクラスの対数オッズです。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0XMwnDOp-llH"
      },
      "source": [
        "### 非表示ユニット\n",
        "\n",
        "上記のモデルには、入力と出力の間に 2 つの中間または「非表示」レイヤーがあります。出力数（ユニット数、ノード数、またはニューロン数）はレイヤーの表現空間の次元で、言い換えると、内部表現を学習する際にネットワークが許可された自由の量と言えます。\n",
        "\n",
        "モデルの非表示ユニット数やレイヤー数が増えるほど（より高次元の表現空間）、ネットワークはより複雑な表現を学習できますが、ネットワークの計算がより高価となり、トレーニングデータでのパフォーマンスを改善してもテストデータでのパフォーマンスは改善されない不要なパターンが学習されることになります。この現象を*過適合*と呼び、これについては後の方で説明します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L4EqVWg4-llM"
      },
      "source": [
        "### 損失関数とオプティマイザ\n",
        "\n",
        "モデルをトレーニングするには、損失関数とオプティマイザが必要です。これは二項分類問題であり、モデルは確率（シグモイドアクティベーションを持つ単一ユニットレイヤー）を出力するため、`binary_crossentropy` 損失関数を使用します。\n",
        "\n",
        "これは、損失関数の唯一の選択肢ではありませんが、たとえば、 `mean_squared_error` を使用することもできます。ただし、一般的には、確率を扱うには `binary_crossentropy` の方が適しているといえます。これは、確率分布間、またはこのケースではグランドトゥルース分布と予測間の「距離」を測定するためです。\n",
        "\n",
        "後の方で、回帰問題（家の価格を予測するなど）を考察する際に、平均二乗誤差と呼ばれる別の損失関数の使用方法を確認します。\n",
        "\n",
        "では、オプティマイザと損失関数を使用するようにモデルを構成します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Mr0GP-cQ-llN"
      },
      "outputs": [],
      "source": [
        "model.compile(optimizer='adam',\n",
        "              loss=tf.losses.BinaryCrossentropy(from_logits=True),\n",
        "              metrics=[tf.metrics.BinaryAccuracy(threshold=0.0, name='accuracy')])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hCWYwkug-llQ"
      },
      "source": [
        "## 検証セットを作成する\n",
        "\n",
        "トレーニングの際、モデルが遭遇したことのないデータでモデルの精度を確認したいと思います。そこで、元のトレーニングデータから 10,000 個のサンプルを取り出して*検証セット*を作成します（ここでテストセットを使用しないのは、トレーニングデータのみを使用してモデルの構築と調整を行った上で、テストデータを一度だけ使用して精度を評価することを目標としているからです）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-NpcXY9--llS"
      },
      "outputs": [],
      "source": [
        "x_val = train_examples[:10000]\n",
        "partial_x_train = train_examples[10000:]\n",
        "\n",
        "y_val = train_labels[:10000]\n",
        "partial_y_train = train_labels[10000:]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "35jv_fzP-llU"
      },
      "source": [
        "## モデルをトレーニングする\n",
        "\n",
        "モデルを 512 サンプルのミニバッチで 40 エポック、トレーニングします。これは、`x_train` と `y_train` テンソルのすべてのサンプルを 40 回イテレーションします。トレーニング中、検証セットの 10,000 個のサンプルで、モデルの損失と精度を監視します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tXSGrjWZ-llW"
      },
      "outputs": [],
      "source": [
        "history = model.fit(partial_x_train,\n",
        "                    partial_y_train,\n",
        "                    epochs=40,\n",
        "                    batch_size=512,\n",
        "                    validation_data=(x_val, y_val),\n",
        "                    verbose=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9EEGuDVuzb5r"
      },
      "source": [
        "## モデルを評価する\n",
        "\n",
        "モデルがどのように実行するか見てみましょう。2 つの値が返されます。損失（誤差、値が低いほど良）と精度です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zOMKywn4zReN"
      },
      "outputs": [],
      "source": [
        "results = model.evaluate(test_data, test_labels)\n",
        "\n",
        "print(results)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "z1iEXVTR0Z2t"
      },
      "source": [
        "このかなり単純なアプローチで、約 87％ の精度が達成されます。より高度なアプローチを使えば、95％ に近づくでしょう。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5KggXVeL-llZ"
      },
      "source": [
        "## 経時的な精度と損失のグラフを作成する\n",
        "\n",
        "`model.fit()` は、トレーニング中に発生したすべての情報を詰まったディクショナリを含む `History` オブジェクトを返します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VcvSXvhp-llb"
      },
      "outputs": [],
      "source": [
        "history_dict = history.history\n",
        "history_dict.keys()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nRKsqL40-lle"
      },
      "source": [
        "トレーニングと検証中に監視されている各メトリックに対して 1 つずつ、計 4 つのエントリがあります。このエントリを使用して、トレーニングと検証の損失とトレーニングと検証の精度を比較したグラフを作成することができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nGoYf2Js-lle"
      },
      "outputs": [],
      "source": [
        "acc = history_dict['accuracy']\n",
        "val_acc = history_dict['val_accuracy']\n",
        "loss = history_dict['loss']\n",
        "val_loss = history_dict['val_loss']\n",
        "\n",
        "epochs = range(1, len(acc) + 1)\n",
        "\n",
        "# \"bo\" is for \"blue dot\"\n",
        "plt.plot(epochs, loss, 'bo', label='Training loss')\n",
        "# b is for \"solid blue line\"\n",
        "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n",
        "plt.title('Training and validation loss')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Loss')\n",
        "plt.legend()\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6hXx-xOv-llh"
      },
      "outputs": [],
      "source": [
        "plt.clf()   # clear figure\n",
        "\n",
        "plt.plot(epochs, acc, 'bo', label='Training acc')\n",
        "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n",
        "plt.title('Training and validation accuracy')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Accuracy')\n",
        "plt.legend()\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oFEmZ5zq-llk"
      },
      "source": [
        "このグラフでは、点はトレーニングの損失と精度を表し、実線は検証の損失と精度を表します。\n",
        "\n",
        "トレーニングの損失がエポックごとに*下降*し、トレーニングの精度がエポックごとに*上昇*していることに注目してください。これは、勾配下降最適化を使用しているときに見られる現象で、イテレーションごとに希望する量を最小化します。\n",
        "\n",
        "これは検証の損失と精度には当てはまりません。20 エポック当たりでピークに達しているようです。これが過適合の例で、モデルが、遭遇したことのないデータよりもトレーニングデータで優れたパフォーマンスを発揮する現象です。この後、モデルは過度に最適化し、テストデータに*一般化*しないトレーニングデータ*特有*の表現を学習します。\n",
        "\n",
        "このような特定のケースについては、約 20 エポック以降のトレーニングを単に停止することで、過適合を回避することができます。この処理をコールバックによって自動的に行う方法については、別の記事で説明します。"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "tf2_text_classification.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
